﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Mx.Ipn.Escom.TT20080015.Modelo.Memoria
{
    public enum Transformada { ZERO_HOT = 0, ONE_HOT = 1, ZERO = 0, ONE = 1 }

    [Serializable]
    class BAMAlphaBeta : Memory
    {
        /* Linear Associator Modificado formado por los patrones de salida y. */
        List<List<byte>> LAy;

        /* Memoria Asociativa Alfa-Beta max. */
        List<List<byte>> max;

        /* Memoria Asociativa Alfa-Beta min. */
        List<List<byte>> min;

        public BAMAlphaBeta(int input, int output)
        {
            xSize = input;
            ySize = output;
        }
        public BAMAlphaBeta() { }

        protected void init(List<Asociacion> a)
        {
            max = new List<List<byte>>(xSize + a.Count);
            min = new List<List<byte>>(xSize + a.Count);
            List<byte> plistMax;
            List<byte> plistMin;
            for (int i = 0; i < xSize + a.Count; i++) // (One/Zero)-hot
            {
                plistMax = new List<byte>();
                plistMin = new List<byte>();
                for (int j = 0; j < xSize + a.Count; j++) // (One/Zero)-hot
                {
                    plistMax.Add(0);
                    plistMin.Add(2);
                }
                max.Add(plistMax);
                min.Add(plistMin);
            }

            LAy = new List<List<byte>>();
            List<byte> temp;

            for (int i = 0; i < a[0].y.Count; ++i)
            {
                temp = new List<byte>();
                for (int j = 0; j < a.Count; ++j)
                    temp.Add(a[j].y[i]);
                LAy.Add(temp);
            }
        }

        /**
         * Transformada vectorial de expansión.
         * X = tau^e(x,h)
         */
        public List<byte> Expansion(List<byte> values, int pos, Transformada tipo, int size)
        {
            byte type = (tipo == Transformada.ONE_HOT) ? (byte)0 : (byte)1;
            List<byte> res = new List<byte>();
            res.AddRange(values);
            for (int i = 0; i < size; i++) res.Add(type);
            res[pos] = (tipo == Transformada.ONE_HOT) ? (byte)1 : (byte)0;

            return res;
        }
        public List<byte> Expansion(List<byte> values, Transformada tipo, int size)
        {
            byte type = (tipo == Transformada.ONE) ? (byte)1 : (byte)0;
            List<byte> res = new List<byte>();
            res.AddRange(values);
            for (int i = 0; i < size - values.Count; i++) res.Add(type);

            return res;
        }
        /**
         * Transformada vectorial de contracción.
         * X = tau^e(x,h)
         */
        public List<byte> Contraccion(List<byte> v, int index, int size)
        {
            return v.GetRange(index, size); ;
        }
        /**
         * Alfa V
         */
        public List<List<byte>> AlfaMax(List<Asociacion> a)
        {
            List<byte> expansion;
            byte aux;

            for (int i = 0; i < max.Count; i++)
                for (int j = 0; j < max[0].Count; j++)
                {
                    for (int k = 0; k < a.Count; k++)
                    {
                        expansion = Expansion(a[k].x, xSize + k, Transformada.ONE_HOT, a.Count);
                        aux = Alfa(expansion[i], expansion[j]);
                        max[i][j] |= aux;
                        if (aux == 2) break;
                    }
                    if (3 == max[i][j]) max[i][j] = 2;
                }
            return max;
        }
        public List<List<byte>> BetaMax()
        {
            return null;
        }
        public List<List<byte>> AlfaMin(List<Asociacion> a)
        {
            List<byte> expansion;
            byte aux;

            for (int i = 0; i < min.Count; i++)
                for (int j = 0; j < min[0].Count; j++)
                    for (int k = 0; k < a.Count; k++)
                    {
                        expansion = Expansion(a[k].x, xSize + k, Transformada.ZERO_HOT, a.Count);
                        aux = Alfa(expansion[i], expansion[j]);
                        if (aux < min[i][j]) min[i][j] = aux;
                        if (aux == 0) break;
                    }
            return min;
        }
        public List<List<byte>> BetaMin()
        {
            return null;
        }
        /**
         * Alpha: AxA -> B
         * -------------------------
         *  x       y     Alpha(x,y)
         * -------------------------
         *  0       0        1
         *  0       1        0
         *  1       0        2
         *  1       1        1
         *  ------------------------
         */
        public static byte Alfa(byte x, byte y)
        {
            if (x == y) return 1;
            if (x == 0 && y == 1) return 0;
            return 2;
        }
        /**
         * Beta: BxA -> A
         * -------------------------
         *  x       y     Beta(x,y)
         * -------------------------
         *  0       0        0
         *  0       1        0
         *  1       0        0
         *  1       1        1
         *  2       0        1
         *  2       1        1
         *  ------------------------
         */
        public static byte Beta(byte x, byte y)
        {
            if ((x == 0) || (x == 1 && y == 0)) return 0;
            return 1;
        }

        public override void Learn(List<Asociacion> a)
        {
            init(a);
            AlfaMax(a);
            AlfaMin(a);
        }
        public List<byte> Output(List<byte> xt)
        {
            byte aux, aux1;
            List<byte> f = Expansion(xt, Transformada.ONE, max.Count);
            List<byte> g = Expansion(xt, Transformada.ZERO, max.Count);
            List<byte> result = new List<byte>();
            List<byte> result2 = new List<byte>();

            foreach (List<byte> m in max)
            {
                aux1 = 2;
                for (int i = 0; i < f.Count; ++i)
                {
                    aux = Beta(m[i], f[i]);
                    if (aux < aux1) aux1 = aux;
                    if (aux == 0) break;
                }
                result.Add(aux1);
            }
            List<byte> r = Contraccion(result, xt.Count, result.Count - xt.Count);
            if (isXHot(r, Transformada.ONE_HOT)) return Memory.MatrixperVector(LAy, r);

            foreach (List<byte> m in min)
            {
                aux1 = 0;
                for (int i = 0; i < g.Count; ++i)
                {
                    aux = Beta(m[i], g[i]);
                    if (aux > aux1) aux1 = aux;
                    if (aux == 2) break;
                }
                result2.Add(aux1);
            }
            List<byte> s = Contraccion(result2, xt.Count, result.Count - xt.Count);
            if (isXHot(s, Transformada.ZERO_HOT)) return Memory.MatrixperVector(LAy, s);

            return Memory.MatrixperVector(LAy, And(r, Negar(s)));//LAy x t
        }
        public List<byte> Negar(List<byte> b)
        {
            List<byte> res = new List<byte>(b.Count);
            byte aux;
            for (int i = 0; i < b.Count; i++)
            {
                aux = (b[i] == 0) ? (byte)1 : (byte)0x0;
                res.Add(aux);
            }
            return res;
        }
        protected static List<byte> And(List<byte> v1, List<byte> v2)
        {
            List<byte> res = new List<byte>();
            for (int i = 0; i < v2.Count; i++) res.Add((byte)(v1[i] & v2[i]));

            return res;
        }
        protected static bool isXHot(List<byte> vector, Transformada tipo)
        {
            int no = 0;
            foreach (int i in vector) if (i == (int)tipo) no += i;
            return (no == 1);
        }
        public override List<byte> Output(Asociacion xp)
        {
            xp.y = Output(xp.x);
            return xp.y;
        }
        public bool equals(List<byte> a1, List<byte> a2)
        {
            bool esIgual = true;
            if (a1.Equals(a2)) return true;
            if (a1.Count != a2.Count) return false;
            for (int i = 0; i < a1.Count; i++)
            {
                esIgual = a1[i] == a2[i];
                if (!esIgual) break;
            }
            return esIgual;
        }
        public override string ToString()
        {
            string str = "";
            for (int i = 0; i < max.Count; i++)
            {
                for (int j = 0; j < max[0].Count; j++)
                    str += max[i][j] + ",";
                str += "\n";
            }

            str += "\n";
            for (int i = 0; i < min.Count; i++)
            {
                for (int j = 0; j < min[0].Count; j++)
                    str += min[i][j] + ",";
                str += "\n";
            }

            str += "\n";
            for (int i = 0; i < LAy.Count; i++)
            {
                for (int j = 0; j < LAy[0].Count; j++)
                    str += LAy[i][j] + ",";
                str += "\n";
            }
            return str;
        }
    }
}
